import datetime
import json
import logging
import uuid

import pymongo
import requests
from qcloud_cos import CosConfig, CosS3Client
from wechatpy import WeChatClient

from base.connection import mongodb_util
from base.request import ApiBaseHandler
from common.definition import ret_msg
from conf import config
from conf.config import DOMAIN
from helper.api_helper import ApiHelper
from helper.payment.pay_util import generator_trade_no
from moviepy.editor import VideoFileClip

logger = logging.getLogger("wechat.handlers.discover")


class UploadImg(ApiBaseHandler):
    async def post(self):
        imgs = self.request.files.get("img", None)
        img_uls = []
        size = int(self.request.headers.get("Content-Length")) / 1024
        logger.info(f"size={size}")
        if size > 9000:
            return self.write(ret_msg(code=-1, error_message="上传图片不能大于9M！"))

        if imgs:
            secret_id = config.COS_APP_SECRET_ID
            secret_key = config.COS_APP_SECRET_KEY
            region = "ap-guangzhou"
            token = ""
            scheme = "https"
            cfg = CosConfig(
                Region=region,
                Secret_id=secret_id,
                Secret_key=secret_key,
                Token=token,
                Scheme=scheme,
            )
            client = CosS3Client(cfg)
            base_url = (
                "https://"
                + "style-1251902022."
                + "file.myqcloud.com/static/img/discover/"
            )
            for img in imgs:
                if not img.filename.lower().endswith(
                    tuple([".jpg", ".png", ".jpeg", ".bmp"])
                ):
                    return self.write(ret_msg(code=-1, error_message="无效的图片格式"))
                filename = (
                    str(uuid.uuid4()).replace("-", "")
                    + "."
                    + img.filename.split(".")[-1]
                )
                response = client.put_object(
                    Bucket=config.COS_BUCKET + "-" + config.COS_APP_ID,
                    Body=img["body"],
                    Key="static/img/discover/" + filename,
                )
                url = base_url + filename
                img_uls.append(url)
            logger.debug(f"response={response}")
        return self.write(ret_msg(data=img_uls))


class CreatePosts(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        title = self.get_parameter("title", None)
        img_urls = self.get_parameter("img_urls", [])
        content = self.get_parameter("content", None)
        lng = self.get_parameter("lng", None)
        lat = self.get_parameter("lat", None)
        address = self.get_parameter("address", None)
        if not title:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数标题"))

        if not content:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数内容"))

        if img_urls:
            if not isinstance(img_urls, list):
                logger.info("type(img_urls)=%s" % type(img_urls))
                img_urls = json.loads(img_urls)

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        ua = session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        if not ua:
            logger.error(f"user_agency表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="没有发帖权限，如有疑问请在公众号留言！"))

        agency_id = ua.ebf_agency_id
        agency = session.query(Agency).filter_by(ebf_agency_id=agency_id).first()
        if not agency:
            logger.error(f"agency表无记录: open_id={open_id} agency_id={agency_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message=f"合伙人编号错误: {agency_id}！"))

        agency_type = agency.ebf_agency_type
        sub_agency_type = agency.ebf_agency_sub_type
        agency_name = agency.ebf_agency_name
        if not lng:
            lng = (
                float(agency.ebf_agency_longitude) if agency.ebf_agency_longitude else 0
            )
            lat = float(agency.ebf_agency_latitude) if agency.ebf_agency_latitude else 0
            address = agency.ebf_agency_address
        else:
            lng = float(lng)
            lat = float(lat)
        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        now = datetime.datetime.now()
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        data = dict(
            _id=generator_trade_no(),  # 贴子主键
            title=title,  # 标题
            content=content,  # 内容
            user=user_info,  # 发帖人信息
            comments=[],  # 评论
            create_time=now,  # 创建时间
            imgs=img_urls,  # 图片地址列表
            comment_nums=0,  # 评论数
            upvote_nums=0,  # 点赞数
            read_nums=0,  # 阅读人数
            is_delete=0,  # 是否删除
            agency_id=agency_id,  # 合伙人编号
            agency_name=agency_name,  # 商家名称
            address=address,  # 商家地址
            agency_type=agency_type,  # 合伙人类型
            sub_agency_type=sub_agency_type,  # 合伙人子类型
            location=[lng, lat],
            lng=lng,  # 经度
            lat=lat,  # 纬度
            upvotes=[],  # 点赞用户信息
            readers=[],  # 阅读者信息
        )
        try:
            db = mongodb_util.db
            db.sdec_posts.insert(data)
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="发布失败, 请稍后重试"))

        session.close()
        return self.write(ret_msg())


class Posts(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        page_index = int(self.get_argument("page_index", 0))
        page_size = int(self.get_argument("page_size", 10))
        lng = float(self.get_parameter("lng", 0))
        lat = float(self.get_parameter("lat", 0))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        posts = []
        try:
            db = mongodb_util.db
            result = db.sdec_posts.aggregate(
                [
                    {
                        "$geoNear": {
                            "near": {"type": "Point", "coordinates": [lng, lat]},
                            "distanceField": "distance",
                            "spherical": True,
                        }
                    },
                    {"$sort": {"distance": 1, "create_time": -1}},
                    {"$skip": page_start},
                    {"$limit": page_size},
                ]
            )
            for p in result:
                p["create_time"] = p["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
                p["distance"] = round(p["distance"], 2)
                # 判断用户是否已对该贴点赞
                if open_id in [u["open_id"] for u in p["upvotes"]]:
                    # 已点赞
                    is_upvote = 1
                else:
                    # 未点赞
                    is_upvote = 0

                # 判断用户是否有删除帖子权限
                if (
                    open_id == p["user"]["open_id"]
                    or open_id in config.POSTS_DELETE_PERMISSION
                ):
                    is_delete_permission = 1
                else:
                    is_delete_permission = 0
                p["is_delete_permission"] = is_delete_permission
                p["is_upvote"] = is_upvote
                p.pop("comments")
                p.pop("readers")
                p.pop("upvotes")
                posts.append(p)
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="获取贴子列表失败, 请稍后重试"))

        return self.write(ret_msg(data=posts))


class PostsUpvote(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        try:
            db = mongodb_util.db
            result = db.sdec_posts.find_one({"_id": posts_id})
            if not result:
                return self.write(ret_msg(code=-1, error_message="贴子ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        upvotes = result["upvotes"]
        upvote_nums = result["upvote_nums"]
        if open_id in [u["open_id"] for u in upvotes]:
            # 已点赞
            upvote_index = [u["open_id"] for u in upvotes].index(open_id)
            upvotes.pop(upvote_index)
            upvote_nums -= 1
            is_upvote = 0
        else:
            # 未点赞
            upvotes.append(user_info)
            upvote_nums += 1
            is_upvote = 1

        try:
            db.sdec_posts.update_one(
                {"_id": posts_id},
                {"$set": {"upvote_nums": upvote_nums, "upvotes": upvotes}},
            )
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        context = dict(upvote_nums=upvote_nums, is_upvote=is_upvote)
        session.close()
        return self.write(ret_msg(data=context))


class CreateComment(ApiBaseHandler):
    def send_text_msg(self, touser, content_text):
        app_id = config.MP_APP_ID
        app_secret = config.MP_APP_SECRET
        access_token = self.get_access_token()
        try:
            client = WeChatClient(app_id, app_secret, access_token)
            client.message.send_text(touser, content_text)
            logger.info(f"发送文本信息通知成功: touser={touser}")
        except Exception as e:
            logger.error(f"发送文本信息通知失败: err={e} touser={touser}")

    async def post(self):
        open_id = await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        comment_id = self.get_parameter("comment_id", None)
        content = self.get_parameter("content", None)
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        if not content:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数评论内容"))

        try:
            db = mongodb_util.db
            result = db.sdec_posts.find_one({"_id": posts_id})
            if not result:
                return self.write(ret_msg(code=-1, error_message="贴子ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        comments = result["comments"]
        comment_nums = result["comment_nums"]
        title = result["title"]
        nickname = user.ebf_user_nickname
        t = datetime.datetime.now().strftime("%m{m}%d{d} %H:%M").format(m="月", d="日")
        url = f"https://{DOMAIN}/wx/template/message?title={posts_id}"
        detail = f'\n\n<a href="{url}">点击查看详情</a>'
        if comment_id:
            comment_index = [c["id"] for c in comments].index(comment_id)
            reply_comment = comments[comment_index]
            c = reply_comment["content"]
            if reply_comment["user"]["open_id"] == open_id:
                reply_nickname = None
                reply_id = None
            else:
                reply_nickname = reply_comment["user"]["nickname"]
                reply_id = comment_id
                user_id = reply_comment["user"]["open_id"]
                content_text = f"“{nickname}”于{t}"
                content_text += (
                    f"回复了您在【{title}】文章中的评论\n\n您的评论：{c}\n回复内容：{content}" + detail
                )
                self.send_text_msg(user_id, content_text)
        else:
            reply_nickname = None
            reply_id = None
            if open_id != result["user"]["open_id"]:
                author_id = result["user"]["open_id"]
                content_text = (
                    f"“{nickname}”于{t}"
                    + f"评论了您标题为【{title}】的文章\n\n评论内容：{content}"
                    + detail
                )
                self.send_text_msg(author_id, content_text)

        headurl = user.ebf_user_headurl
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        comments.append(
            dict(
                content=content,
                user=user_info,
                id=generator_trade_no(),
                reply_nickname=reply_nickname,
                reply_id=reply_id,
                create_time=datetime.datetime.now(),
            )
        )
        comment_nums += 1
        try:
            db.sdec_posts.update_one(
                {"_id": posts_id},
                {"$set": {"comment_nums": comment_nums, "comments": comments}},
            )
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        new_comments = []
        for comment in comments[:-11:-1]:
            comment["create_time"] = comment["create_time"].strftime(
                "%Y年%m月%d日 %H:%M:%S"
            )
            new_comments.append(comment)
        context = dict(comment_nums=comment_nums, comments=new_comments)
        session.close()
        return self.write(ret_msg(data=context))


class PostsDetail(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        try:
            db = mongodb_util.db
            result = db.sdec_posts.find_one({"_id": posts_id}, {"comments": 0})
            if not result:
                return self.write(ret_msg(code=-1, error_message="贴子ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        readers = result.get("readers", [])
        read_nums = result["read_nums"]
        upvotes = result["upvotes"]
        collectors = result["collectors"]
        if open_id not in [u["open_id"] for u in readers]:
            # 未阅读
            read_nums += 1  # 阅读量加1
            readers.append(user_info)
            try:
                db.sdec_posts.update_one(
                    {"_id": posts_id},
                    {"$set": {"read_nums": read_nums, "readers": readers}},
                )
            except Exception as e:
                logger.error(e)
                session.close()
                return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        # 判断用户是否有删除帖子权限
        if (
            open_id == result["user"]["open_id"]
            or open_id in config.POSTS_DELETE_PERMISSION
        ):
            is_delete_permission = 1
        else:
            is_delete_permission = 0
        result["is_delete_permission"] = is_delete_permission
        result["read_nums"] = read_nums
        result["create_time"] = result["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
        if open_id in [u["open_id"] for u in upvotes]:
            is_upvote = 1  # 已点赞
        else:
            is_upvote = 0  # 未点赞

        if open_id in [u["open_id"] for u in collectors]:
            is_collect = 1  # 已收藏
        else:
            is_collect = 0  # 未收藏

        author_id = result["user"]["open_id"]
        user_focus = json.loads(user.ebf_user_focus) if user.ebf_user_focus else []
        if author_id in user_focus:
            is_subscribe = 1  # 已关注
        else:
            is_subscribe = 0  # 未关注

        if open_id == author_id:
            is_own = 1  # 自己的文章
        else:
            is_own = 0  # 别人的文章
        result["is_upvote"] = is_upvote
        result["is_collect"] = is_collect
        result["is_subscribe"] = is_subscribe
        result["is_own"] = is_own
        result["goods_id"] = result["goods_id"] if result.get("goods_id", None) else []
        result.pop("upvotes")
        result.pop("readers", None)
        result.pop("collectors", None)
        session.close()
        return self.write(ret_msg(data=result))


class PostsComments(ApiBaseHandler):
    async def get(self):
        await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        try:
            db = mongodb_util.db
            results = db.sdec_posts.aggregate(
                [
                    {"$match": {"_id": posts_id}},
                    {"$unwind": "$comments"},
                    {"$sort": {"comments.create_time": -1}},
                    {"$skip": page_start},
                    {"$limit": page_size},
                    {"$group": {"_id": "$_id", "comments": {"$push": "$comments"}}},
                ]
            )
            if not results:
                return self.write(ret_msg(code=-1, error_message="贴子ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        context = []
        for r in results:
            comments = r["comments"]
            for c in comments:
                c["create_time"] = c["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
            context.append(r)

        return self.write(ret_msg(data=context[0] if context else {}))


class MongoHandle(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        if open_id != "o7qyl0vKx54jB05FjmxNw76SJAPE":
            return self.write(ret_msg(code=-1, error_message="权限不足"))

        try:
            db = mongodb_util.db
            db.sdec_posts.remove({})
            db.sdec_posts.ensure_index([("location", "2dsphere")])  # 创建索引
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        return self.write(ret_msg())


class UserPosts(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        page_index = int(self.get_argument("page_index", 0))
        page_size = int(self.get_argument("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        posts = []
        try:
            db = mongodb_util.db
            result = (
                db.sdec_posts.find({"is_delete": {"$ne": 1}, "user.open_id": open_id})
                .sort("_id", -1)
                .skip(page_start)
                .limit(page_size)
            )
            for p in result:
                p["create_time"] = p["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
                # 判断用户是否已对该贴点赞
                if open_id in [u["open_id"] for u in p["upvotes"]]:
                    # 已点赞
                    is_upvote = 1
                else:
                    # 未点赞
                    is_upvote = 0
                p.pop("comments")
                p.pop("readers")
                p.pop("upvotes")
                p["is_delete_permission"] = 1
                p["is_upvote"] = is_upvote
                posts.append(p)
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="获取贴子列表失败, 请稍后重试"))

        return self.write(ret_msg(data=posts))


class DeletePosts(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        try:
            db = mongodb_util.db
            posts = db.sdec_posts.find_one({"_id": posts_id})
            if not posts:
                return self.write(ret_msg(code=-1, error_message="帖子不存在!"))

            # 判断用户是否有删除帖子权限
            if (
                open_id != posts["user"]["open_id"]
                and open_id not in config.POSTS_DELETE_PERMISSION
            ):
                return self.write(ret_msg(code=-1, error_message="权限不足!"))
            result = db.sdec_posts.delete_one({"_id": posts_id})
            count = result.deleted_count
            logger.info(f"删除的帖子数目: count={count}")
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="删除失败!"))

        return self.write(ret_msg(data=count))


class UploadResource(ApiBaseHandler):
    async def post(self):
        resource = self.request.files.get("resource", None)
        file_type = self.get_parameter("type", "img")
        resource_uls = []
        size = int(self.request.headers.get("Content-Length")) / 1024
        logger.info(f"size={size}")
        if size > 100000:
            return self.write(ret_msg(code=-1, error_message="上传文件不能大于100M！"))

        if resource:
            secret_id = config.COS_APP_SECRET_ID
            secret_key = config.COS_APP_SECRET_KEY
            region = "ap-guangzhou"
            token = ""
            scheme = "https"
            cfg = CosConfig(
                Region=region,
                Secret_id=secret_id,
                Secret_key=secret_key,
                Token=token,
                Scheme=scheme,
            )
            client = CosS3Client(cfg)
            if file_type == "img":
                base_url = "https://" + "style-1251902022." + "file.myqcloud.com/"
                key = "static/img/discover/"
            else:
                base_url = "https://" + "style-1251902022." + "file.myqcloud.com/"
                key = "static/video/discover/"

            for r in resource:
                if file_type == "img" and not r.filename.lower().endswith(
                    tuple([".jpg", ".png", ".jpeg", ".bmp"])
                ):
                    return self.write(ret_msg(code=-1, error_message="无效的图片格式"))

                if file_type == "video" and not r.filename.lower().endswith(
                    tuple([".mp4", ".mov", ".avi", ".mid", "rmvb", ".flv", ".3gp"])
                ):
                    return self.write(ret_msg(code=-1, error_message="无效的视频格式"))

                prefix = str(uuid.uuid4()).replace("-", "") + "."
                filename = key + prefix + r.filename.split(".")[-1]
                # 视频抽取封面图片
                if file_type == "video":
                    # 保存视频文件：static/video/discover/ 目录要预先创建
                    nfs = "" if config.DEBUG else "/jfs/project/wechat/"
                    with open(nfs + filename, "wb") as file:
                        file.write(r["body"])
                    # 抽取GIF
                    gif_filename = key + prefix + "gif"
                    logger.debug(f"gif_filename={gif_filename}")
                    with VideoFileClip(nfs + filename).subclip(1, 2) as clip:
                        clip.write_gif(nfs + gif_filename, fps=15)
                    # 上传视频封面动图
                    with open(nfs + gif_filename, "rb") as f:
                        response = client.put_object(
                            Bucket=config.COS_BUCKET + "-" + config.COS_APP_ID,
                            Body=f,
                            Key=gif_filename,
                        )
                        url = base_url + gif_filename
                        resource_uls.append(url)
                        logger.debug(f"UploadResource: response={response}")
                # 上传文件
                response = client.put_object(
                    Bucket=config.COS_BUCKET + "-" + config.COS_APP_ID,
                    Body=r["body"],
                    Key=filename,
                )
                url = base_url + filename
                resource_uls.append(url)
                logger.debug(f"UploadResource: response={response}")

        return self.write(ret_msg(data=resource_uls))


class WriteArticle(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        title = self.get_parameter("title", None)
        file_urls = self.get_parameter("file_urls", [])
        content = self.get_parameter("content", None)
        lng = float(self.get_parameter("lng", 0))
        lat = float(self.get_parameter("lat", 0))
        address = self.get_parameter("address", None)
        goods_id = self.get_parameter("goods_id", [])
        status = int(self.get_parameter("status", 1))
        old_id = self.get_parameter("id", 0)  # 草稿ID

        if not title and status:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数标题"))

        # if not content and status:
        #    return self.write(ret_msg(code=-1, error_message="缺少请求参数内容"))

        if not isinstance(goods_id, list):
            logger.error(f"goods_id={goods_id}")
            return self.write(ret_msg(code=-1, error_message="推荐商品参数错误"))

        if file_urls:
            if not isinstance(file_urls, list):
                logger.info("type(img_urls)=%s" % type(file_urls))
                file_urls = json.loads(file_urls)

        class User(object):
            pass

        class Agency(object):
            pass

        class UserAgency(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
                {"db_table": "ebt_agency", "model": Agency},
                {"db_table": "ebt_user_agency", "model": UserAgency},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.info(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="请先关注【莱支好酒】公众号"))

        ua = session.query(UserAgency).filter_by(ebf_ua_bind_user_id=open_id).first()
        if ua:
            agency_id = ua.ebf_agency_id
            agency = session.query(Agency).filter_by(ebf_agency_id=agency_id).first()
            if not agency:
                logger.error(f"agency表无记录: open_id={open_id} agency_id={agency_id}")
                session.close()
                return self.write(
                    ret_msg(code=-1, error_message=f"合伙人编号错误: {agency_id}！")
                )

            agency_type = agency.ebf_agency_type
            sub_agency_type = agency.ebf_agency_sub_type
            agency_name = agency.ebf_agency_name
            if not address:
                lng = (
                    float(agency.ebf_agency_longitude)
                    if agency.ebf_agency_longitude
                    else 0
                )
                lat = (
                    float(agency.ebf_agency_latitude)
                    if agency.ebf_agency_latitude
                    else 0
                )
                address = agency.ebf_agency_address
        else:
            agency_id = None
            agency_type = None
            sub_agency_type = None
            agency_name = None

        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        now = datetime.datetime.now()
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        data = dict(
            _id=generator_trade_no(),  # 贴子主键
            title=title,  # 标题
            content=content,  # 内容
            user=user_info,  # 发帖人信息
            comments=[],  # 评论
            create_time=now,  # 创建时间
            file=file_urls,  # 文件地址列表
            comment_nums=0,  # 评论数
            upvote_nums=0,  # 点赞数
            read_nums=0,  # 阅读人数
            is_delete=0,  # 是否删除
            agency_id=agency_id,  # 合伙人编号
            agency_name=agency_name,  # 商家名称
            address=address,  # 商家地址
            agency_type=agency_type,  # 合伙人类型
            sub_agency_type=sub_agency_type,  # 合伙人子类型
            location=[lng, lat],
            lng=lng,  # 经度
            lat=lat,  # 纬度
            upvotes=[],  # 点赞用户信息
            readers=[],  # 阅读者信息
            collectors=[],  # 收藏者信息
            collect_nums=0,  # 收藏人数
            status=status,  # 帖子状态 0=存草稿 1=发布
            goods_id=goods_id,  # 推荐商品编号列表
        )
        try:
            db = mongodb_util.db
            db.sdec_posts.insert(data)
            if int(old_id):
                result = db.sdec_posts.delete_one({"_id": old_id})
                count = result.deleted_count
                logger.info(f"删除的帖子数目: count={count}")
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="发布失败, 请稍后重试"))

        session.close()
        return self.write(ret_msg())


class Article(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        t = int(
            self.get_argument("t", 2)
        )  # 0=表示用户说 1=表示关注 2=表示行家评 3=表示个人日记 4=收藏 5=个人草稿
        user_id = self.get_argument("user_id", None)  # 关注的特定个人账号ID
        page_index = int(self.get_argument("page_index", 0))
        page_size = int(self.get_argument("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        article = []

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        if not user_id:
            user_id = open_id

        user = session.query(User).filter_by(ebf_user_id=user_id).first()
        if not user:
            logger.error(f"用户表无记录: user_id={user_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="请先关注【莱支好酒】公众号"))

        user_focus = json.loads(user.ebf_user_focus) if user.ebf_user_focus else []
        try:
            db = mongodb_util.db
            if t == 1:
                # 关注
                filters = {
                    "is_delete": 0,
                    "status": 1,
                    "user.open_id": {"$in": user_focus},
                }
            elif t == 2:
                # 行家评
                filters = {
                    "is_delete": 0,
                    "status": 1,
                    "agency_type": 3,
                    "sub_agency_type": 2,
                }
            elif t == 3:
                # 个人日记
                filters = {"is_delete": 0, "status": 1, "user.open_id": user_id}
            elif t == 4:
                # 收藏
                filters = {
                    "is_delete": 0,
                    "status": 1,
                    "user.open_id": {"$ne": user_id},
                    "collectors.open_id": user_id,
                }
            elif t == 5:
                # 个人草稿
                filters = {"is_delete": 0, "status": 0, "user.open_id": user_id}
            elif not t:
                # 用户说
                filters = {
                    "is_delete": 0,
                    "status": 1,
                    "$or": [
                        {"agency_type": {"$ne": 3}},
                        {"$and": [{"agency_type": 3}, {"sub_agency_type": {"$ne": 2}}]},
                    ],
                }
            else:
                logger.error(f"请求参数类型错误: t={t}")
                session.close()
                return self.write(ret_msg(code=-1, error_message=f"请求参数类型错误:{t}"))

            if t == 5 and open_id != user_id:
                # 自己只能看自己个人草稿
                result = []
            else:
                result = (
                    db.sdec_posts.find(filters)
                    .sort("_id", pymongo.DESCENDING)
                    .skip(page_start)
                    .limit(page_size)
                )

            for p in result:
                p["create_time"] = p["create_time"].strftime("%Y年%m月%d日 %H:%M:%S")
                # 判断用户是否已对该贴点赞
                if open_id in [u["open_id"] for u in p["upvotes"]]:
                    # 已点赞
                    is_upvote = 1
                else:
                    # 未点赞
                    is_upvote = 0

                # 判断用户是否有删除帖子权限
                if (
                    open_id == p["user"]["open_id"]
                    or open_id in config.POSTS_DELETE_PERMISSION
                ):
                    is_delete_permission = 1
                else:
                    is_delete_permission = 0
                p["is_delete_permission"] = is_delete_permission
                p["is_upvote"] = is_upvote
                p.pop("comments")
                p.pop("readers")
                p.pop("upvotes")
                p.pop("collectors", 0)
                article.append(p)
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="获取贴子列表失败, 请稍后重试"))

        session.close()
        return self.write(ret_msg(data=article))


class CollectArticle(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        posts_id = self.get_parameter("id", None)
        if not posts_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数贴子ID"))

        try:
            db = mongodb_util.db
            result = db.sdec_posts.find_one({"_id": posts_id})
            if not result:
                return self.write(ret_msg(code=-1, error_message="贴子ID错误"))
        except Exception as e:
            logger.error(e)
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        headurl = user.ebf_user_headurl
        nickname = user.ebf_user_nickname
        user_info = dict(open_id=open_id, nickname=nickname, headurl=headurl)
        collectors = result.get("collectors", [])
        collect_nums = result.get("collect_nums", 0)
        if open_id in [u["open_id"] for u in collectors]:
            # 已收藏
            collect_index = [u["open_id"] for u in collectors].index(open_id)
            collectors.pop(collect_index)
            collect_nums -= 1
            is_collect = 0
        else:
            # 未收藏
            collectors.append(user_info)
            collect_nums += 1
            is_collect = 1

        try:
            db.sdec_posts.update_one(
                {"_id": posts_id},
                {"$set": {"collect_nums": collect_nums, "collectors": collectors}},
            )
        except Exception as e:
            logger.error(e)
            session.close()
            return self.write(ret_msg(code=-1, error_message="系统繁忙, 请稍后重试"))

        context = dict(collect_nums=collect_nums, is_collect=is_collect)
        session.close()
        return self.write(ret_msg(data=context))


class Subscribe(ApiBaseHandler):
    async def post(self):
        open_id = await self.get_open_id()
        user_id = self.get_parameter("user_id", None)
        is_subscribe = None
        if not user_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数用户ID"))

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=open_id).first()
        author = session.query(User).filter_by(ebf_user_id=user_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        if not author:
            logger.error(f"用户表无记录: open_id={user_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="行家账号错误"))

        if open_id != user_id:
            authors = user.ebf_user_focus
            author_fans = author.ebf_user_fans
            authors = json.loads(authors) if authors else []
            author_fans = json.loads(author_fans) if author_fans else []

            if user_id in authors:
                # 已关注
                user_index = authors.index(user_id)
                fan_index = author_fans.index(open_id)
                authors.pop(user_index)
                author_fans.pop(fan_index)
                is_subscribe = 0
            else:
                # 未关注
                authors.append(user_id)
                author_fans.append(open_id)
                is_subscribe = 1

            user.ebf_user_focus = json.dumps(authors)
            author.ebf_user_fans = json.dumps(author_fans)
            session.commit()
        else:
            session.close()
            return self.write(ret_msg(code=-1, error_message="自己不能关注自己"))

        session.close()
        return self.write(ret_msg(data=is_subscribe))


class Test(ApiBaseHandler):
    async def get(self):
        media_id = self.get_parameter("media_id", None)
        logger.info(f"media_id={media_id}")
        return self.write(ret_msg(data=media_id))


class Upload(ApiBaseHandler):
    def get_img_content(self, access_token, media_id):
        base_url = "https://api.weixin.qq.com/cgi-bin/media/get"
        full_url = base_url + f"?access_token={access_token}&media_id={media_id}"
        response = requests.get(url=full_url, timeout=30)
        return response

    async def post(self):
        await self.get_open_id()
        access_token = self.get_access_token()
        media_id = self.get_parameter("media_id", None)
        if not media_id:
            return self.write(ret_msg(code=-1, error_message="缺少请求参数media_id"))

        response = self.get_img_content(access_token, media_id)
        result = response.content
        headers = response.headers  # 获取响应头
        content_disposition = headers.get("Content-disposition", None)
        if not content_disposition:
            result = result.decode()
            if "42001" in result or "40001" in result:
                logger.error(f"error_msg1={result}")
                counter = 0
                while "42001" in result or "40001" in result:
                    # 当access_token无效或者过期时重新获取并再次请求
                    if counter > 20:
                        break

                    counter += 1
                    access_token = self.get_access_token()
                    response = self.get_img_content(access_token, media_id)
                    result = response.content
                    headers = response.headers  # 获取响应头
                    content_disposition = headers.get("Content-disposition", None)
                    if not content_disposition:
                        result = result.decode()
                        logger.error(f"error_msg2={result} counter={counter}")
                    else:
                        break
                if not content_disposition:
                    logger.error(f"error_msg3={result}")
                    return self.write(ret_msg(code=-1, error_message="系统繁忙，请稍后重试"))
            elif "40007" in result:
                logger.error(f"media_id无效")
                return self.write(ret_msg(code=-1, error_message="media_id无效"))
            else:
                logger.error(f"error_msg={result}")
                return self.write(ret_msg(code=-1, error_message="上传失败请重试"))

        logger.info(f"content_disposition={content_disposition}")
        file_name = content_disposition[
            content_disposition.find("=") + 1 :
        ]  # 获取filename的值
        logger.info(f"file_name={file_name}")
        if file_name.lower().split(".")[-1][:-1] not in ["jpg", "png", "jpeg", "bmp"]:
            return self.write(ret_msg(code=-1, error_message="无效的图片格式"))

        # 上传文件到腾讯云存储
        secret_id = config.COS_APP_SECRET_ID
        secret_key = config.COS_APP_SECRET_KEY
        region = "ap-guangzhou"
        token = ""
        scheme = "https"
        cfg = CosConfig(
            Region=region,
            Secret_id=secret_id,
            Secret_key=secret_key,
            Token=token,
            Scheme=scheme,
        )
        client = CosS3Client(cfg)
        file_url = (
            "https://" + "style-1251902022." + "file.myqcloud.com/static/img/discover/"
        )
        key = "static/img/discover/"
        filename = (
            str(uuid.uuid4()).replace("-", "") + "." + file_name.split(".")[-1][:-1]
        )
        res = client.put_object(
            Bucket=config.COS_BUCKET + "-" + config.COS_APP_ID,
            Body=result,
            Key=key + filename,
        )
        url = file_url + filename
        logger.info(f"response={res}")
        logger.info(f"url={url}")
        return self.write(ret_msg(data=url))


class GoodsInfo(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        keyword = self.get_parameter("keyword", None)
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size
        if keyword:
            keyword = json.dumps(keyword)
            keyword = keyword.replace('"', "")
            keyword = keyword.replace("\\", "_")

        class Goods(object):
            pass

        class GoodsOrder(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "sdet_order", "model": GoodsOrder},
                {"db_table": "sdet_goods", "model": Goods},
            ]
        )

        filters = dict(sdef_order_is_delete=0, sdef_order_device_id=open_id)
        if keyword:
            orders = (
                session.query(GoodsOrder)
                .filter_by(**filters)
                .filter(GoodsOrder.sdef_order_goods.ilike(f"%{keyword}%"))
                .filter(GoodsOrder.sdef_order_type != 3)
                .order_by(GoodsOrder.sdef_order_id.desc())[
                    page_start : page_start + page_size
                ]
            )
        else:
            orders = (
                session.query(GoodsOrder)
                .filter_by(**filters)
                .filter(GoodsOrder.sdef_order_type != 3)
                .order_by(GoodsOrder.sdef_order_id.desc())[
                    page_start : page_start + page_size
                ]
            )

        goods_list = []
        for o in orders:
            order_goods = json.loads(o.sdef_order_goods)
            for g in order_goods:
                goods_id = g["id"]
                if goods_id in [goods["id"] for goods in goods_list]:
                    continue

                goods_record = (
                    session.query(Goods).filter_by(sdef_goods_id=goods_id).first()
                )
                # 跳过下架/隐藏/删除商品
                if (
                    goods_record.sdef_goods_status != 1
                    or goods_record.sdef_goods_is_visible != 1
                    or goods_record.sdef_goods_is_delete == 1
                ):
                    continue
                # 用商品表里的数据
                goods = dict(
                    id=goods_id,
                    price=str(goods_record.sdef_goods_price),
                    img=json.loads(goods_record.sdef_goods_thumbnail)[0],
                    name=goods_record.sdef_goods_name,
                )
                goods_list.append(goods)

        return self.write(ret_msg(data=goods_list))


class Fans(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        user_id = self.get_parameter("user_id", None)
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size

        if not user_id:
            user_id = open_id

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=user_id).first()
        own = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id} user_id={user_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        fans = (
            json.loads(user.ebf_user_fans)[page_start : page_start + page_size]
            if user.ebf_user_fans
            else []
        )
        own_focus = json.loads(own.ebf_user_focus) if own.ebf_user_focus else []
        fans_info_list = []
        for u_id in fans:
            u = session.query(User).filter_by(ebf_user_id=u_id).first()
            if not u:
                logger.error(f"用户表无记录: u_id={u_id}")
                continue

            try:
                db = mongodb_util.db
                article_nums = db.sdec_posts.find(
                    {"is_delete": 0, "user.open_id": u_id}
                ).count()
            except Exception as e:
                logger.error(f"error={e}")
                article_nums = 0

            u_fans = json.loads(u.ebf_user_fans) if u.ebf_user_fans else []
            u_focus = json.loads(u.ebf_user_focus) if u.ebf_user_focus else []
            if open_id in u_focus:
                subscribed = 1
            else:
                subscribed = 0

            if u_id in own_focus:
                subscribe = 1
            else:
                subscribe = 0
            fan_info = dict(
                user_id=u.ebf_user_id,
                headurl=u.ebf_user_headurl,
                nickname=u.ebf_user_nickname,
                article_nums=article_nums,
                subscribed=subscribed,  # 别人是否关注了自己
                subscribe=subscribe,  # 自己是否关注了别人
                fans_nums=len(u_fans),
            )
            fans_info_list.append(fan_info)

        session.close()
        return self.write(ret_msg(data=fans_info_list))


class Focus(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        user_id = self.get_parameter("user_id", None)
        page_index = int(self.get_parameter("page_index", 0))
        page_size = int(self.get_parameter("page_size", 10))
        ApiHelper.inspect_common_params(page_index, page_size)
        page_start = page_index * page_size

        if not user_id:
            user_id = open_id

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=user_id).first()
        own = session.query(User).filter_by(ebf_user_id=open_id).first()
        if not user:
            logger.error(f"用户表无记录: open_id={open_id} user_id={user_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        focus = (
            json.loads(user.ebf_user_focus)[page_start : page_start + page_size]
            if user.ebf_user_focus
            else []
        )
        own_focus = json.loads(own.ebf_user_focus) if own.ebf_user_focus else []
        focus_info_list = []
        for u_id in focus:
            u = session.query(User).filter_by(ebf_user_id=u_id).first()
            if not u:
                logger.error(f"用户表无记录: u_id={u_id}")
                continue

            try:
                db = mongodb_util.db
                article_nums = db.sdec_posts.find(
                    {"is_delete": 0, "user.open_id": u_id}
                ).count()
            except Exception as e:
                logger.error(f"error={e}")
                article_nums = 0

            u_fans = json.loads(u.ebf_user_fans) if u.ebf_user_fans else []
            u_focus = json.loads(u.ebf_user_focus) if u.ebf_user_focus else []
            if open_id in u_focus:
                subscribed = 1
            else:
                subscribed = 0

            if u_id in own_focus:
                subscribe = 1
            else:
                subscribe = 0
            fan_info = dict(
                user_id=u.ebf_user_id,
                headurl=u.ebf_user_headurl,
                nickname=u.ebf_user_nickname,
                article_nums=article_nums,
                subscribed=subscribed,  # 别人是否关注了自己
                subscribe=subscribe,  # 自己是否关注了别人
                fans_nums=len(u_fans),
            )
            focus_info_list.append(fan_info)

        session.close()
        return self.write(ret_msg(data=focus_info_list))


class Account(ApiBaseHandler):
    async def get(self):
        open_id = await self.get_open_id()
        user_id = self.get_parameter("user_id", None)
        if not user_id:
            user_id = open_id

        class User(object):
            pass

        session = self.get_db_session(
            [
                {"db_table": "ebt_user", "model": User},
            ]
        )

        user = session.query(User).filter_by(ebf_user_id=user_id).first()
        if not user:
            logger.error(f"用户表无记录: user_id={user_id}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="用户表无记录"))

        fans_nums = len(json.loads(user.ebf_user_fans)) if user.ebf_user_fans else 0
        focus_nums = len(json.loads(user.ebf_user_focus)) if user.ebf_user_focus else 0
        try:
            db = mongodb_util.db
            result = db.sdec_posts.aggregate(
                [
                    {"$match": {"user.open_id": user_id}},
                    {"$group": {"_id": None, "total": {"$sum": "$upvote_nums"}}},
                ]
            )
            upvote_nums = 0
            for r in result:
                upvote_nums = r["total"]
        except Exception as e:
            logger.error(f"error={e}")
            session.close()
            return self.write(ret_msg(code=-1, error_message="系统繁忙，请重新打开"))

        accout_info = dict(
            fans_nums=fans_nums,
            focus_nums=focus_nums,
            upvote_nums=upvote_nums,
            open_id=user_id,
            headurl=user.ebf_user_headurl,
            nickname=user.ebf_user_nickname,
        )
        session.close()
        return self.write(ret_msg(data=accout_info))
